#include <iostream>
using namespace std;

//创建一个类
class person
{
public:
//1、构造函数：作用在于创建对象时为对象的成员属性赋值
//没有返回值，也不用写void
//函数名与类名相同
//可以有参数，因此可以发生重载
//程序在调用对象时会自动调用一次构造函数，而且只会调用一次
//如果我们不写构造函数，编译器会提供空的构造函数
//类名(){}

//构造函数的分类
    //(1)按参数分：有参构造函数和无参构造函数
    //(2)按类型分：普通构造函数和拷贝构造函数

    person() //无参构造函数
    {
        cout << "person的无参构造函数" << endl;
    }

    person(int a,int h) //有参构造函数
    {
        age = a;
        height = new int(h);
        cout << "person的有参构造函数" << endl;
    }
    
    //初始化列表的写法
    // person (int a,int h):age(a),height(new int(h))
    // {
    //     cout << "person的有参构造函数" << endl;
    // }

    person(const person &p)
    {
        age = p.age;
        //height = p.height; //编译器默认实现的就是这行代码，即浅拷贝：简单的赋值拷贝操作
        //如果不利用深拷贝在堆区创建新内存，会导致浅拷贝带来的重复释放堆区问题
        height = new int (*p.height); // 深拷贝 ：在堆区重新申请空间，进行拷贝操作
        cout << "person的拷贝构造函数" << endl;
    }

//2、析构函数：作用在于对象销毁前系统自动调用，执行一些清理工作
//没有返回值，也不用写void
//函数名与类名相同，只需在函数名前加上~
//不可以有参数，因此不可以发生重载
//程序在调用对象时会自动调用一次析构函数，而且只会调用一次
//如果我们不写析构函数，编译器会提供空的析构函数
//~类名(){}

    ~person()
    {   
        //析构代码，将堆区开辟数据做释放操作
        if(height!=NULL)
        {
            delete height;
            height = NULL;
        }
        cout << "person的析构函数" << endl;
    }

    int age;
    int *height = NULL;//创建一个指针
};


//构造函数的调用
void test1()
{   
    //(1)括号法
    person p;//默认调用无参构造函数
    person p1(10,160);//调用有参构造函数
    person p2(p1);//调用拷贝构造函数

    //注意事项
    //调用默认构造函数时候，不要加()
    //person p1()，这行代码编译器会认为是一个函数的声明，不会认为在创建对象

    //(2)显式法
    person p3;//默认调用无参构造函数
    person p4 = person(20,160);//调用有参构造函数
    person p5 = person(p4);//调用拷贝构造函数

    //Person(20)  单独写就是匿名对象 当前行结束之后，系统会立即回收掉匿名对象（马上析构）
    //注意事项：不能利用拷贝构造函数 初始化匿名对象 编译器认为是对象声明
    //person (p5);//有无括号都一样  person (p5) == person p5;

    //(3)隐式转换法
    //person p6 = (30,160); // Person p6 = Person(10); 
    //person p7 = p6; // Person p7 = Person(p6);
}



//拷贝构造函数调用时机有三种情况:
//(1)使用一个已经创建完毕的对象来初始化一个新对象
void test2()
{
    person p1(60,160);
    person p2(p1);//调用构造函数
}
//(2)值传递的方式给函数参数传值
void dowork(person p)
{

}
void test3()
{
    person p1;
    dowork(p1);
}
//(3)以值方式返回局部对象
person dowork2()
{
    person p1;
    return p1;
}
void test4()
{
    person p = dowork2();

}



//构造函数调用规则
// 默认情况下，c++编译器至少给一个类添加3个函数
// 1．默认构造函数  (无参，函数体为空)
// 2．默认析构函数  (无参，函数体为空)
// 3．默认拷贝构造函数，对属性进行值拷贝

// 构造函数调用规则如下：
// 如果用户定义有参构造函数，c++不在提供默认无参构造，但是会提供默认拷贝构造
// 如果用户定义拷贝构造函数，c++不会再提供其他普通构造函数






//浅拷贝与深拷贝
//总结：如果属性有在堆区开辟的，一定要自己提供拷贝构造函数，防止浅拷贝带来的问题
void test5()
{
    person p1(60, 170);
    person p2(p1);

    cout << "p1: " << p1.age << " " << *p1.height << endl;
    cout << "p2: " << p2.age << " " << *p2.height << endl;
}


int main()
{

    //test1();
    //test2();
    //test3();
    //test4();
    test5();

    return 0;
}